package com.tjs.netty.initial;

import org.apache.curator.framework.CuratorFramework;
import org.apache.zookeeper.CreateMode;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;

import com.tjs.netty.constant.Constants;
import com.tjs.netty.handler.ServerHandler;
import com.tjs.netty.zk.ZookeeperUtil;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.Delimiters;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.handler.timeout.IdleStateHandler;

@Component
public class NettyInital implements ApplicationListener<ContextRefreshedEvent> {
	
	@Override
	public void onApplicationEvent(ContextRefreshedEvent event) {
		try {
			//String hostAddress = InetAddress.getLocalHost().getHostAddress();
			run(Constants.HOST, Constants.PORT, Constants.WEIGHT);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * 第一个,通常被称为“老板”,接受传入的连接。 第二个,通常被称为“工人”,处理接受的交通连接一旦老板接受连接,注册接受连接到工人。
	 * 有多少线程使用和它们是如何映射到创建渠道取决于EventLoopGroup通过构造函数实现,甚至可能是可配置的。 线程组创建默认是处理器的两倍
	 * MultithreadEventLoopGroup 源码中有
	 */
	public void run(String host, int port, int weight) {
		EventLoopGroup bossGroup = new NioEventLoopGroup();// 连接处理group
		EventLoopGroup workerGroup = new NioEventLoopGroup();// 事件处理group

		try {
			ServerBootstrap b = new ServerBootstrap();
			b.group(bossGroup, workerGroup); // 绑定处理group
			b.channel(NioServerSocketChannel.class);
			b.childHandler(new ChannelInitializer<SocketChannel>() { // 处理新连接
				@Override
				protected void initChannel(SocketChannel ch) throws Exception {
					ch.pipeline().addLast(new DelimiterBasedFrameDecoder(Integer.MAX_VALUE, Delimiters.lineDelimiter()[0]));
					ch.pipeline().addLast(new StringDecoder());
					ch.pipeline().addLast(new IdleStateHandler(Constants.readerIdleTimeSeconds, 
								Constants.writerIdleTimeSeconds, Constants.allIdleTimeSeconds));
					ch.pipeline().addLast(new ServerHandler());
					ch.pipeline().addLast(new StringEncoder());
				}
			});
			b.option(ChannelOption.SO_BACKLOG, 128);
			b.childOption(ChannelOption.SO_KEEPALIVE, true);
			// 注册到zookeeper服务上
			registerToZookeeper(host, port, weight);
			ChannelFuture f = b.bind(host, port).sync();
			f.channel().closeFuture().sync();
			System.out.println("关闭了");
		} catch (InterruptedException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			workerGroup.shutdownGracefully();
			bossGroup.shutdownGracefully();
		}
	}

	public static void registerToZookeeper(String hostAddress ,int port, int weight) throws Exception {
		CuratorFramework client = ZookeeperUtil.getZookerClient(Constants.WORK_SPACE);
			
		String path = "/" + hostAddress + "#" + port + "#" + weight + "#"  ;
		
		if (client.checkExists().forPath(path) != null) {
			client.delete().deletingChildrenIfNeeded().forPath(path);
		}
		client.create().creatingParentContainersIfNeeded().withMode(CreateMode.EPHEMERAL_SEQUENTIAL).forPath(path);
	}

}
